`include "defines.v"

module arbiter_to_axi (
    input  wire                             clk,
    input  wire                             rst,

    input  wire[`RAM_ADDR_WIDTH - 1:0]      iram_addr,
    output wire[`IRAM_DATA_WIDTH - 1:0]     iram_rdata,

    input  wire                             iram_valid,
    output wire                             iram_ready,

    input  wire                             dram_wen,
    input  wire[`RAM_ADDR_WIDTH - 1:0]      dram_addr,
    input  wire[`RAM_DATA_WIDTH - 1:0]      dram_wdata,
    input  wire[`RAM_DATA_WIDTH / 8 - 1:0]  dram_wmask,
    output wire[`RAM_DATA_WIDTH - 1:0]      dram_rdata,
    input  wire[2:0]                        dram_rw_size,

    input  wire                             dram_valid,
    output wire                             dram_ready,

    ////////////////////////////axi interface
    // aw
    output wire[`AXI_ID_WIDTH - 1:0]        awid,
    output wire[`AXI_ADDR_WIDTH - 1:0]      awaddr,
    output wire[7:0]                        awlen,
    output wire[2:0]                        awsize,
    output wire[1:0]                        awburst,
    output wire                             awlock,
    output wire[3:0]                        awcache,
    output wire[2:0]                        awprot,
    output wire[3:0]                        awqos,
    output wire[3:0]                        awregion,
    output wire[`AXI_USER_WIDTH-1:0]        awuser,
    output wire                             awvalid,
    input  wire                             awready,
    // w
    output wire[`AXI_DATA_WIDTH - 1:0]      wdata,
    output wire[`AXI_DATA_WIDTH / 8 - 1:0]  wstrb,
    output wire                             wlast,
    output wire[`AXI_USER_WIDTH-1:0]        wuser,
    output wire                             wvalid,
    input  wire                             wready,
    // b
    input  wire[3:0]                        bid,
    input  wire[1:0]                        bresp,
    input  wire[`AXI_USER_WIDTH-1:0]        buser,
    input  wire                             bvalid,
    output wire                             bready,
    // ar
    output wire[`AXI_ID_WIDTH - 1:0]        arid,
    output wire[`AXI_ADDR_WIDTH - 1:0]      araddr,
    output wire[7:0]                        arlen,
    output wire[2:0]                        arsize,
    output wire[1:0]                        arburst,
    output wire                             arlock,
    output wire[3:0]                        arcache,
    output wire[2:0]                        arprot,
    output wire[3:0]                        arqos,
    output wire[3:0]                        arregion,
    output wire[`AXI_USER_WIDTH-1:0]        aruser,
    output wire                             arvalid,
    input  wire                             arready,
    // r
    input  wire[`AXI_ID_WIDTH - 1:0]        rid,
    input  wire[`AXI_DATA_WIDTH - 1:0]      rdata,
    input  wire[1:0]                        rresp,
    input  wire                             rlast,
    input  wire[`AXI_USER_WIDTH-1:0]        ruser,
    input  wire                             rvalid,
    output wire                             rready
);
    reg             inst_buffer_flag;
    wire            inst_buffer_flag_value;
    reg [63: 0]     inst_buffer;
    wire[63: 0]     inst_buffer_value;

    // ar def
    wire inst_addr_ok;
    wire inst_addr_valid;

    reg  inst_load_state;
    wire inst_load_state_value;

    wire data_addr_ok;
    wire data_addr_valid;

    reg  data_load_state;
    wire data_load_state_value;

    // r def
    wire r_shake_ok;
    wire r_inst_ok;
    wire r_data_ok;

    // aw w b
    reg  data_store_state_1;
    reg  data_store_state_0;
    wire data_store_state_1_value;
    wire data_store_state_0_value;

    wire aw_data_store_valid;
    wire aw_data_store_ok;
    wire w_data_store_valid;
    wire w_data_store_ok;
    wire b_data_store_ready;
    wire b_data_store_ok;
    /**********************************************************************************************
    *   ar:
    ***********************************************************************************************/



    //当mem访存的时候，暂停取指
    // assign inst_addr_valid = inst_valid_i == 1'b1
    //                       &&(inst_load_state == 1'b0 || inst_load_state == 1'b1 && r_inst_ok == 1'b1)
    //                       && data_load_state_value == 1'b0
    //                       && data_store_state_0_value == 1'b0 && data_store_state_1_value == 1'b0 ? 1'b1 : 1'b0;
    assign inst_addr_valid = iram_valid == 1'b1
                          && inst_buffer_flag   == 1'b0
                          && inst_load_state    == 1'b0
                          && data_load_state    == 1'b0 && data_addr_valid     == 1'b0 
                          && data_store_state_0 == 1'b0 && aw_data_store_valid == 1'b0 
                          && data_store_state_1 == 1'b0 && w_data_store_valid  == 1'b0 ? 1'b1 : 1'b0;

    assign inst_addr_ok = inst_addr_valid == 1'b1 && arready == 1'b1 ? 1'b1 : 1'b0;

    assign inst_load_state_value = inst_load_state == 1'b0 && inst_addr_ok == 1'b1 ? 1'b1 :
                                   inst_load_state == 1'b1 &&    r_inst_ok == 1'b1 ? 1'b0 : inst_load_state;
    
    // assign data_addr_valid = dram_valid == 1'b1 && dram_wen == 1'b0 && data_load_state == 1'b0 ? 1'b1 : 1'b0;
    assign data_addr_valid = dram_valid == 1'b1 && dram_wen == 1'b0 && data_load_state == 1'b0 && inst_load_state == 1'b0 ? 1'b1 : 1'b0;

    assign data_addr_ok    = data_addr_valid == 1'b1 && arready == 1'b1                        ? 1'b1 : 1'b0;

    assign data_load_state_value = data_load_state == 1'b0 && data_addr_ok == 1'b1 ? 1'b1 :
                                   data_load_state == 1'b1 &&    r_data_ok == 1'b1 ? 1'b0 : data_load_state;
    /**********************************************************************************************
    *   r:
    ***********************************************************************************************/
    // assign r_shake_ok = rready & rvalid;
    assign r_shake_ok = rready & rvalid & rlast & ~rresp[0] & ~rresp[1];
    assign r_inst_ok  = r_shake_ok == 1'b1 && rid == 4'h0 ? 1'b1 : 1'b0;
    assign r_data_ok  = r_shake_ok == 1'b1 && rid == 4'h1 ? 1'b1 : 1'b0;

    wire dram_ing;
    // assign dram_ing = data_load_state_value | data_store_state_0_value | data_store_state_1_value;
    assign dram_ing = dram_valid == 1'b1;

    assign inst_buffer_flag_value = inst_buffer_flag == 1'b0 && dram_ing == 1'b1 && r_inst_ok == 1'b1 ? 1'b1 :
                                    inst_buffer_flag == 1'b1 && dram_ing == 1'b0                      ? 1'b0 : inst_buffer_flag;

    assign inst_buffer_value = inst_buffer_flag == 1'b0 && dram_ing == 1'b1 && r_inst_ok == 1'b1 ? rdata : inst_buffer;
    
    /**********************************************************************************************
    *   aw:
    *   w:
    *   b:
    ***********************************************************************************************/

    // assign aw_data_store_valid      = dram_valid & dram_wen & ~data_store_state_1 ? 1'b1 : 1'b0;
    assign aw_data_store_valid      = dram_valid & dram_wen & ~data_store_state_1 & ~inst_load_state ? 1'b1 : 1'b0;
    assign aw_data_store_ok         = aw_data_store_valid & awready               ? 1'b1 : 1'b0;
    assign data_store_state_1_value = aw_data_store_ok == 1'b1 ? 1'b1 :
                                       b_data_store_ok == 1'b1 ? 1'b0 : data_store_state_1;

    // assign w_data_store_valid       = dram_valid & dram_wen & ~data_store_state_0 ? 1'b1 : 1'b0;
    assign w_data_store_valid       = dram_valid & dram_wen & ~data_store_state_0 & ~inst_load_state ? 1'b1 : 1'b0;
    assign w_data_store_ok          = w_data_store_valid & wready                 ? 1'b1 : 1'b0;
    assign data_store_state_0_value = w_data_store_ok == 1'b1 ? 1'b1 :
                                      b_data_store_ok == 1'b1 ? 1'b0 : data_store_state_0;

    assign b_data_store_ready       = data_store_state_1 & data_store_state_0 ? 1'b1 : 1'b0;
    // assign b_data_store_ok          = b_data_store_ready & bvalid             ? 1'b1 : 1'b0;
    assign b_data_store_ok          = b_data_store_ready && bvalid && bid == 4'b0001 && bresp == 2'b00 ? 1'b1 : 1'b0;

    always @(posedge clk) begin
        if (rst == 1'b1)
        begin
            inst_load_state     <= 0;
            data_load_state     <= 0;
            data_store_state_1  <= 0;
            data_store_state_0  <= 0;

            inst_buffer_flag    <= 0;
            inst_buffer         <= 0;
        end
        else
        begin
            inst_load_state     <= inst_load_state_value;
            data_load_state     <= data_load_state_value;
            data_store_state_1  <= data_store_state_1_value;
            data_store_state_0  <= data_store_state_0_value;

            inst_buffer_flag    <= inst_buffer_flag_value;
            inst_buffer         <= inst_buffer_value;
        end
    end 
    /**********************************************************************************************
    * cpu interface
    ***********************************************************************************************/
    //inst
    assign iram_rdata = inst_buffer_flag == 1'b0 ?       rdata[`IRAM_DATA_WIDTH - 1: 0] :
                        inst_buffer_flag == 1'b1 ? inst_buffer[`IRAM_DATA_WIDTH - 1: 0] : 0;

    assign iram_ready = ~inst_buffer_flag & ~dram_ing & r_inst_ok
                      |  inst_buffer_flag & ~dram_ing;

    //data
    assign dram_ready   = data_load_state    & r_data_ok
                        | data_store_state_0 & data_store_state_1 & bvalid;
    assign dram_rdata   = rdata;

    /**********************************************************************************************
    * axi interface
    ***********************************************************************************************/
    // ar
    assign arid     = data_addr_valid == 1'b1 ? 4'h1 : 4'h0;
    // assign araddr   = data_addr_valid == 1'b1 ? {dram_addr[`RAM_ADDR_WIDTH - 1:0]} : {iram_addr[`RAM_ADDR_WIDTH - 1:0]};
    assign araddr   = data_addr_valid == 1'b1 ? {dram_addr[`RAM_ADDR_WIDTH - 1:3], 3'h0} : {iram_addr[`RAM_ADDR_WIDTH - 1:3], 3'h0};
    assign arlen    = 8'b0;
    // assign arsize   = inst_addr_valid ? 3'b010 : dram_rw_size;
    assign arsize   = dram_rw_size;
    assign arburst  = 2'b01;
    assign arlock   = 1'b0;
    assign arcache  = 4'b0;
    assign arprot   = 3'b0;
    assign arqos    = 4'b0;
    assign arregion = 4'b0;
    assign aruser   = 1'b0;
    assign arvalid  = inst_addr_valid | data_addr_valid ? 1'h1 : 1'h0;

    // r
    assign rready   = 1'h1;

    // aw
    assign awid     = 4'b0001;
    assign awaddr   = dram_addr;
    assign awlen    = 8'b0;
    //000 1Byte
    //001 2Byte
    //010 4Byte
    //011 8Byte
    assign awsize   = dram_rw_size;
    assign awburst  = 2'b01;
    assign awlock   = 1'b0;
    assign awcache  = 4'b0;
    assign awprot   = 3'b0;
    assign awqos    = 4'b0;
    assign awregion = 4'b0;
    assign awuser   = 1'b0;
    assign awvalid  = aw_data_store_valid;

    // w
    assign wdata    = dram_wdata;
    assign wstrb    = dram_wmask;
    assign wlast    = 1'b1;
    assign wuser    = 1'b0;
    assign wvalid   = w_data_store_valid;

    // b
    assign bready   = b_data_store_ready;
endmodule
